package com.ruowei.modules.foodOrder.web.rest;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.ruowei.common.error.exception.CommonException;
import com.ruowei.modules.foodOrder.domain.Order;
import com.ruowei.modules.foodOrder.domain.enumeration.OrderStatus;
import com.ruowei.modules.foodOrder.pojo.dto.OrderDetailDTO;
import com.ruowei.modules.foodOrder.pojo.dto.RefundOrderDetailDTO;
import com.ruowei.modules.foodOrder.repository.OrderRepository;
import com.ruowei.modules.foodOrder.service.OrderService;
import com.ruowei.modules.member.domain.Member;
import com.ruowei.modules.sys.domain.annotation.OrderLock;
import com.ruowei.modules.sys.utils.CommonUtils;
import io.swagger.annotations.ApiOperation;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ruowei.common.error.exception.BadRequestAlertException;
import com.ruowei.common.querydsl.OptionalBooleanBuilder;
import com.ruowei.modules.foodOrder.domain.*;
import com.ruowei.modules.foodOrder.repository.*;
import com.ruowei.modules.foodOrder.web.dto.*;
import com.ruowei.modules.sys.utils.AppletTokenUtils;
import io.swagger.annotations.Api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * REST controller for managing {@link Order}.
 */
@RestController
@RequestMapping("/api")
@Transactional
@Api(tags = "点餐订单管理")
public class OrderResource {

    private final Logger log = LoggerFactory.getLogger(OrderResource.class);

    private final OrderRepository orderRepository;
    private final OrderService orderService;
    private final OrderItemRepository orderItemRepository;
    private final RefundApplicationRepository refundApplicationRepository;
    private final ShoppingCartRepository shoppingCartRepository;
    private final MerchantRepository merchantRepository;
    private final MerchandiseRepository merchandiseRepository;
    private final QrAddressRepository qrAddressRepository;
    private final JPAQueryFactory queryFactory;
    private final QOrder QO = QOrder.order;
    private final QOrderItem QOI = QOrderItem.orderItem;
    private final QMerchant QM = QMerchant.merchant;
    private final QShoppingCart QSC = QShoppingCart.shoppingCart;


    public OrderResource(OrderRepository orderRepository, OrderService orderService, OrderItemRepository orderItemRepository, RefundApplicationRepository refundApplicationRepository, ShoppingCartRepository shoppingCartRepository, MerchantRepository merchantRepository, MerchandiseRepository merchandiseRepository, QrAddressRepository qrAddressRepository, JPAQueryFactory queryFactory) {
        this.orderRepository = orderRepository;
        this.orderService = orderService;
        this.orderItemRepository = orderItemRepository;
        this.refundApplicationRepository = refundApplicationRepository;
        this.shoppingCartRepository = shoppingCartRepository;
        this.merchantRepository = merchantRepository;
        this.merchandiseRepository = merchandiseRepository;
        this.qrAddressRepository = qrAddressRepository;
        this.queryFactory = queryFactory;
    }

    @PostMapping("/applet/diancan/createOrder/test")
    @ApiOperation("小程序下单-测试")
    public ResponseEntity<Order> createOrder(HttpServletRequest request, @RequestParam String merchantCode,
                                             @RequestParam BigDecimal amount,
                                             @RequestParam String receiverName,
                                             @RequestParam String receiverPhone,
                                             @RequestParam String receiverAddress,
                                             @RequestParam String remark,
                                             @RequestParam String merchandiseCode,
                                             @RequestParam String merchandiseName,
                                             @RequestParam String specification,
                                             @RequestParam String photos,
                                             @RequestParam Integer quantity,
                                             @RequestParam OrderStatus status) {
        Member member = AppletTokenUtils.getOpenIdByToken(request);
        Order order = new Order();
        order.setOrderCode(CommonUtils.getUUID());
        order.setCreateTime(Instant.now());
        order.setCreateDate(LocalDate.now());
        order.setMemberCode(member.getOpenId());
        order.setMerchantCode(merchantCode);
        order.setTotalAmount(amount);
        order.setPayAmount(amount);
        order.setPayType("微信支付");
        order.setStatus(status);
        order.setReceiverName(receiverName);
        order.setReceiverPhone(receiverPhone);
        order.setReceiverAddress(receiverAddress);
        order.setRemark(remark);
        order.setConfirmStatus(false);
        order.setDeleteStatus(false);
        order.setPaymentTime(Instant.now());
        order = orderRepository.saveAndFlush(order);

        OrderItem item = new OrderItem();
        item.setItemCode(CommonUtils.getUUID());
        item.setOrderCode(order.getOrderCode());
        item.setMerchandiseCode(merchandiseCode);
        item.setMerchandiseName(merchandiseName);
        item.setSpecification(specification);
        item.setPhotos(photos);
        item.setUnitPrice(amount);
        item.setQuantity(quantity);
        item.setOriginUnitPrice(amount);
        orderItemRepository.save(item);
        return ResponseEntity.ok(order);
    }

    @PostMapping("/applet/diancan/createOrder")
    @ApiOperation("小程序下单")
    public ResponseEntity<Map<String, String>> createOrder(@RequestParam String cartCodes,
                                                           @RequestParam String receiverName,
                                                           @RequestParam String receiverPhone,
                                                           @RequestParam String receiverAddress,
                                                           @RequestParam String remark,
                                                           HttpServletRequest request) {
        Optional<QrAddress> qrAddress = qrAddressRepository.findByDetailAddressAndStatus(receiverAddress, true);
        if (!qrAddress.isPresent()) {
            throw new CommonException("地址不存在，请重新选择地址");
        }
        Map<String, String> result = orderService.createOrder(cartCodes, receiverName, receiverPhone, receiverAddress, remark, request);
        return ResponseEntity.ok(result);
    }

    @PostMapping("/applet/repay")
    @ApiOperation("重新支付订单")
    @OrderLock
    public ResponseEntity<Map<String, String>> repay(@RequestParam String code,
                                                     HttpServletRequest request) {
        Map<String, String> result = orderService.repay(code, request);
        return ResponseEntity.ok(result);
    }

    @PostMapping("/applet/cancelOrder")
    @ApiOperation("用户取消订单-商家未接单")
    @OrderLock
    public ResponseEntity<Map<String, String>> cancelOrder(@RequestParam String orderCode, HttpServletRequest request) throws Exception {
        Map<String, String> result = orderService.cancelOrder(orderCode, request);
        return ResponseEntity.ok(result);
    }

    @GetMapping("/applet/diancan/getOrderStatus")
    @ApiOperation("查询订单状态")
    @OrderLock
    public ResponseEntity<Map<String, String>> getOrderStatus(@RequestParam String orderCode, HttpServletRequest request) throws Exception {
        Map<String, String> result = orderService.getOrderStatus(orderCode, request);
        return ResponseEntity.ok(result);
    }

    @PostMapping("/applet/returnApplication")
    @ApiOperation("退款申请")
    @OrderLock
    public ResponseEntity<Map<String, String>> returnApplication(@RequestParam String orderCode,
                                                                 @RequestParam BigDecimal returnAmount,
                                                                 @RequestParam(required = false) String reason,
                                                                 @RequestParam(required = false) String description,
                                                                 HttpServletRequest request) {
        Map<String, String> result = orderService.returnApplication(orderCode, returnAmount, reason, description, request);
        return ResponseEntity.ok(result);
    }

    @GetMapping("/applet/refundOrderDetail")
    @ApiOperation("获取退款订单详情")
    public ResponseEntity<RefundOrderDetailDTO> refundOrderDetail(@RequestParam String orderCode, HttpServletRequest request) {
        RefundOrderDetailDTO detailDTO = orderService.refundOrderDetail(orderCode, request);
        return ResponseEntity.ok(detailDTO);
    }

    @GetMapping("/applet/orderTracking")
    @ApiOperation("订单追踪")
    public ResponseEntity<List<OrderLifeCycle>> orderTracking(@RequestParam String orderCode, HttpServletRequest request) {
        List<OrderLifeCycle> list = orderService.orderTracking(orderCode, request);
        return ResponseEntity.ok(list);
    }

    @GetMapping("/applet/getOrderDetail")
    @ApiOperation("获取订单详情")
    public ResponseEntity<OrderDetailDTO> getOrderDetail(@RequestParam String orderCode, HttpServletRequest request) {
        OrderDetailDTO detailDTO = orderService.getOrderDetail(orderCode, request);
        return ResponseEntity.ok(detailDTO);
    }

    @GetMapping("/orders")
    @ApiOperation(value = "获取订单列表-web")
    public ResponseEntity<Page<OrderListDTO>> getAllOrders(OrderQM qm, Pageable pageable) {
        OptionalBooleanBuilder predicate = new OptionalBooleanBuilder()
            .notEmptyAnd(QM.merchantName::contains, qm.getMerchantName())
            .notEmptyAnd(QO.status::eq, qm.getStatus())
            .notEmptyAnd(QO.receiverName::contains, qm.getReceiverName())
            .notEmptyAnd(QO.createTime::goe, qm.getCreateTimeStart())
            .notEmptyAnd(QO.createTime::loe, qm.getCreateTimeEnd());
        List<OrderListDTO> orderListDTOS = new ArrayList<>();

        JPAQuery<Tuple> jpaQuery = queryFactory.select(QO.orderCode,
            QO.status, QO.totalAmount, QO.payAmount, QO.receiverName, QO.payType, QO.createTime, QO.paymentTime,
            QO.merchantReceiveTime, QO.courierReceiveTime, QO.deliveryTime, QO.receiveTime, QM.merchantName)
            .from(QO)
            .leftJoin(QM).on(QO.merchantCode.eq(QM.merchantCode))
            .where(QO.deleteStatus.eq(false).and(predicate.build()))
            .orderBy(QO.createTime.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize());
        jpaQuery.fetch().forEach(tuple -> {
            orderListDTOS.add(new OrderListDTO(tuple.get(QO.orderCode), tuple.get(QM.merchantName), tuple.get(QO.status),
                tuple.get(QO.totalAmount), tuple.get(QO.receiverName), tuple.get(QO.payAmount), tuple.get(QO.payType),
                tuple.get(QO.createTime), tuple.get(QO.paymentTime), tuple.get(QO.merchantReceiveTime),
                tuple.get(QO.courierReceiveTime), tuple.get(QO.deliveryTime), tuple.get(QO.receiveTime)));

        });
        Page<OrderListDTO> page = new PageImpl<>(orderListDTOS, pageable, jpaQuery.fetchCount());
        return ResponseEntity.ok(page);
    }

    @GetMapping("/orders/detail")
    @ApiOperation(value = "获取订单详情-web")
    public ResponseEntity<OrderDTO> getOrderDetail(@RequestParam String orderCode) {
        Optional<Order> anOrder = orderRepository.findOneByOrderCodeAndDeleteStatus(orderCode, false);
        if (!anOrder.isPresent()) {
            throw new BadRequestAlertException("订单信息不存在");
        }
        OrderDTO orderDTO = new OrderDTO();
        anOrder.ifPresent(order -> {
            orderDTO.setOrderCode(order.getOrderCode());
            List<OrderItemDTO> dtos = new ArrayList<>();
            orderItemRepository.findAllByOrderCode(order.getOrderCode())
                .forEach(orderItem -> {
                    OrderItemDTO dto = new OrderItemDTO();
                    dto.setPhotos(orderItem.getPhotos());
                    dto.setMerchandiseName(orderItem.getMerchandiseName());
                    dto.setSpecification(orderItem.getSpecification());
                    dto.setUnitPrice(orderItem.getUnitPrice());
                    dto.setQuantity(orderItem.getQuantity());
                    dto.setOriginUnitPrice(orderItem.getOriginUnitPrice());
                    dtos.add(dto);

                });
            orderDTO.setDtoList(dtos);
            orderDTO.setStatus(order.getStatus().getMessage());
            orderDTO.setCreateTime(order.getCreateTime());
            orderDTO.setTotalAmount(order.getTotalAmount());
            orderDTO.setPayAmount(order.getPayAmount());
            orderDTO.setPayType(order.getPayType());
            orderDTO.setPaymentTime(order.getPaymentTime());
            orderDTO.setMerchantReceiveTime(order.getMerchantReceiveTime());
            orderDTO.setCourierReceiveTime(order.getCourierReceiveTime());
            orderDTO.setDeliveryTime(order.getDeliveryTime());
            orderDTO.setReceiverName(order.getReceiverName());
            orderDTO.setReceiverPhone(order.getReceiverPhone());
            orderDTO.setReceiverAddress(order.getReceiverAddress());
            orderDTO.setCourierName(order.getCourierName());
            orderDTO.setCourierPhone(order.getCourierPhone());
            orderDTO.setReceiveTime(order.getReceiveTime());
            orderDTO.setRemark(order.getRemark());
            refundApplicationRepository.findFirstByOrderCodeOrderByTimeDesc(order.getOrderCode())
                .ifPresent(refundApplication -> {
                    orderDTO.setReFundPhotos(refundApplication.getPhotos());
                    orderDTO.setRefundTime(refundApplication.getTime());
                    orderDTO.setApplyStatus(refundApplication.getStatus().getMessage());
                    orderDTO.setReturnAmount(refundApplication.getReturnAmount());
                    orderDTO.setHandleTime(refundApplication.getHandleTime());
                    orderDTO.setReason(refundApplication.getReason());
                    orderDTO.setDescription(refundApplication.getDescription());
                    orderDTO.setHandler(refundApplication.getHandler());
                    orderDTO.setHandleRemark(refundApplication.getHandleRemark());
                });
        });
        return ResponseEntity.ok(orderDTO);
    }

    @GetMapping("/orders/getDetail")
    @ApiOperation(value = "订单详情-app")
    public ResponseEntity<OrderSpecDTO> getDetail(@RequestParam String orderCode) {
        OrderSpecDTO dto = new OrderSpecDTO();
        Optional<Order> anOrder = orderRepository.findOneByOrderCodeAndDeleteStatus(orderCode, false);
        if (!anOrder.isPresent()) {
            throw new BadRequestAlertException("订单信息不存在");
        }
        Order order = anOrder.get();
        dto.setOrderCode(order.getOrderCode());
        dto.setReceiverName(order.getReceiverName());
        dto.setReceiverPhone(order.getReceiverPhone());
        dto.setReceiverAddress(order.getReceiverAddress());
        dto.setStatus(order.getStatus());
        dto.setPayAmount(order.getPayAmount());
        dto.setCreateTime(order.getCreateTime());
        dto.setMerchantReceiveTime(order.getMerchantReceiveTime());
        dto.setCourierName(order.getCourierName());
        dto.setCourierPhone(order.getCourierPhone());
        dto.setCourierReceiveTime(order.getCourierReceiveTime());
        dto.setReceiveTime(order.getReceiveTime());
        dto.setRemark(order.getRemark());
        refundApplicationRepository.findFirstByOrderCodeOrderByTimeDesc(order.getOrderCode())
            .ifPresent(refundApplication -> {
                dto.setReason(refundApplication.getReason());
                dto.setTime(refundApplication.getTime());
                dto.setHandleTime(refundApplication.getHandleTime());
            });
        List<GoodsListDTO> goodsList = new ArrayList<>();
        orderItemRepository.findAllByOrderCode(order.getOrderCode())
            .forEach(orderItem -> {
                GoodsListDTO goods = new GoodsListDTO();
                goods.setMerchandiseName(orderItem.getMerchandiseName());
                goods.setPhotos(orderItem.getPhotos());
                goods.setUnitPrice(orderItem.getUnitPrice());
                goods.setPackingFee(orderItem.getPackingFee());
                goods.setQuantity(orderItem.getQuantity());
                goods.setSpecification(orderItem.getSpecification());
                goodsList.add(goods);
            });
        dto.setGoodsList(goodsList);
        return ResponseEntity.ok(dto);
    }

    @GetMapping("/applet/orders/historyOrderList")
    @ApiOperation(value = "查询历史订单列表-小程序")
    public ResponseEntity<List<HistoryOrderDTO>> getHistoryOrder(HttpServletRequest request, @RequestParam(required = false) String status) {
        List<HistoryOrderDTO> historyList = new ArrayList<>();
        QOrder qOrder = QOrder.order;
        BooleanBuilder builder = new BooleanBuilder();
        switch (status) {
            case "进行中": {
                builder.and(qOrder.status.in(OrderStatus.TO_BE_RECEIVED, OrderStatus.MERCHANT_RECEIVED, OrderStatus.COURIER_RECEIVED));
                break;
            }
            case "待支付": {
                builder.and(qOrder.status.eq(OrderStatus.TO_BE_PAID));
                break;
            }
            case "退款": {
                builder.and(qOrder.status.in(OrderStatus.REFUND_APPLYING, OrderStatus.REFUNDING, OrderStatus.REFUND_AGREE, OrderStatus.REFUNDED, OrderStatus.REFUND_FAILED, OrderStatus.REFUND_CLOSED));
                break;
            }
        }
        builder.and(qOrder.memberCode.eq(AppletTokenUtils.getOpenIdByToken(request).getOpenId()));
        builder.and(qOrder.deleteStatus.eq(false));
        queryFactory.selectFrom(qOrder)
            .where(builder)
            .orderBy(qOrder.createTime.desc())
            .fetch()
            .forEach(order -> {
                HistoryOrderDTO historyDTO = new HistoryOrderDTO();
                historyDTO.setOrderCode(order.getOrderCode());
                historyDTO.setStatus(order.getStatus().getMessage());
                historyDTO.setTotalAmount(order.getTotalAmount());
                historyDTO.setPayAmount(order.getPayAmount());
                merchantRepository.findByMerchantCode(order.getMerchantCode())
                    .ifPresent(merchant -> {
                        historyDTO.setMerchantCode(merchant.getMerchantCode());
                        historyDTO.setMerchantName(merchant.getMerchantName());
                        historyDTO.setPhotos(merchant.getPhotos());
                    });
                List<OrderGoodsDTO> dtos = new ArrayList<>();
                orderItemRepository.findAllByOrderCode(order.getOrderCode())
                    .forEach(orderItem -> {
                        OrderGoodsDTO goodsDTO = new OrderGoodsDTO();
                        goodsDTO.setMerchandiseName(orderItem.getMerchandiseName());
                        goodsDTO.setPhotos(orderItem.getPhotos());
                        goodsDTO.setUnitPrice(orderItem.getUnitPrice());
                        goodsDTO.setQuantity(orderItem.getQuantity());
                        dtos.add(goodsDTO);

                    });
                historyDTO.setMerchandiseList(dtos);
                historyList.add(historyDTO);
            });
        return ResponseEntity.ok(historyList);
    }

    @GetMapping("/applet/orders/detail")
    @ApiOperation(value = "获取订单详情-小程序")
    public ResponseEntity<OrderDetailsDTO> getOrder(@RequestParam String orderCode) {
        OrderDetailsDTO detailDTO = new OrderDetailsDTO();
        orderRepository.findOneByOrderCodeAndDeleteStatus(orderCode, false)
            .ifPresent(order -> {
                detailDTO.setCurrentTime(Instant.now());
                detailDTO.setStatus(order.getStatus().getMessage());
                detailDTO.setReceiverName(order.getReceiverName());
                detailDTO.setReceiverPhone(order.getReceiverPhone());
                detailDTO.setReceiverAddress(order.getReceiverAddress());
                detailDTO.setCreateTime(order.getCreateTime());
                detailDTO.setPayType(order.getPayType());
                detailDTO.setCourierCode(order.getCourierCode());
                detailDTO.setCourierName(order.getCourierName());
                detailDTO.setCourierPhone(order.getCourierPhone());
                detailDTO.setRemark(order.getRemark());
                detailDTO.setTotalAmount(order.getTotalAmount());
                detailDTO.setPayAmount(order.getPayAmount());
                merchantRepository.findByMerchantCode(order.getMerchantCode())
                    .ifPresent(merchant -> {
                        detailDTO.setMerchantCode(merchant.getMerchantCode());
                        detailDTO.setMerchantName(merchant.getMerchantName());
                        detailDTO.setPhotos(merchant.getPhotos());
                        detailDTO.setPhone(merchant.getPhone());
                    });
                final BigDecimal[] sum = {new BigDecimal("0.00")};
                List<GoodsListDTO> dtos = new ArrayList<>();
                orderItemRepository.findAllByOrderCode(order.getOrderCode())
                    .forEach(orderItem -> {
                        GoodsListDTO dto = new GoodsListDTO();
                        dto.setMerchandiseName(orderItem.getMerchandiseName());
                        dto.setQuantity(orderItem.getQuantity());
                        dto.setPhotos(orderItem.getPhotos());
                        dto.setUnitPrice(orderItem.getUnitPrice());
                        dto.setSpecification(orderItem.getSpecification());
                        dto.setPackingFee(orderItem.getPackingFee());
                        sum[0] = sum[0].add(new BigDecimal(String.valueOf(orderItem.getPackingFee())));
                        dtos.add(dto);
                    });
                detailDTO.setGoodsList(dtos);
                detailDTO.setTotalPackingFee(sum[0]);
            });
        return ResponseEntity.ok(detailDTO);
    }

}
